/*---------------------------------------------------------------------
 * File name: hal_timer.cpp
 *
 * Copyright (c) <2020-2022>, <ChenLong>
 *
 * All rights reserved.
 *
 * Author: ChenLong
 * Email: worldlong@foxmail.com
 *--------------------------------------------------------------------*/
#include "gd32f30x.h"
#include "hal_timer.h"
#include "hal_config.h"
#include "hal_system.h"
#if defined(USING_RT_THREAD)
#include "rtthread.h" 
#endif
/*
*/
static void (*irq_hook[14])(void *param) = {nullptr};
static void *irq_hook_param[14]={nullptr};
const static struct {
  uint8_t irqn;
  uint8_t priority;
}nvic_table[]={
  //Timer0
#ifdef GD32F30X_HD
  {TIMER0_UP_IRQn, 
   NVIC_IRQ_TIM0_PRIORITY}, 
#else
  {TIMER0_UP_TIMER9_IRQn, 
   NVIC_IRQ_TIM0_PRIORITY},
#endif
  //Timer1
  {TIMER1_IRQn,   
   NVIC_IRQ_TIM1_PRIORITY},
  //Timer2
  {TIMER2_IRQn, 
   NVIC_IRQ_TIM2_PRIORITY},
   //Timer3
  {TIMER3_IRQn,
   NVIC_IRQ_TIM3_PRIORITY},
   //Timer4
  {TIMER4_IRQn,
   NVIC_IRQ_TIM4_PRIORITY},
  //Timer5
  {TIMER5_IRQn, 
   NVIC_IRQ_TIM5_PRIORITY},
  //Timer6
  {TIMER6_IRQn, 
   NVIC_IRQ_TIM6_PRIORITY},
  //Timer7
#ifdef GD32F30X_HD   
  {TIMER7_UP_IRQn, 
   NVIC_IRQ_TIM7_PRIORITY},
#else
  {TIMER7_UP_TIMER12_IRQn, 
   NVIC_IRQ_TIM7_PRIORITY},
#endif
   //Timer8
#ifdef GD32F30X_HD
   {0, 0},
#else
  {TIMER0_BRK_TIMER8_IRQn,
   NVIC_IRQ_TIM8_PRIORITY},
#endif
   //Timer9
#ifdef GD32F30X_HD
   {0, 0},
#else
  {TIMER0_UP_TIMER9_IRQn, 
   NVIC_IRQ_TIM0_PRIORITY},
#endif
   //Timer10
#ifdef GD32F30X_HD
   {0, 0},
#else
  {TIMER0_TRG_CMT_TIMER10_IRQn
   NVIC_IRQ_TIM10_PRIORITY},
#endif
 //Timer11
#ifdef GD32F30X_HD
   {0, 0},
#else
  {TIMER7_BRK_TIMER11_IRQn,
   NVIC_IRQ_TIM11_PRIORITY},
#endif
 //Timer12
#ifdef GD32F30X_HD
   {0, 0},
#else   
  {TIMER7_UP_TIMER12_IRQn,
   NVIC_IRQ_TIM12_PRIORITY},
#endif
 //Timer13
#ifdef GD32F30X_HD
   {0, 0},
#else   
  {TIMER7_TRG_CMT_TIMER13_IRQn,
   NVIC_IRQ_TIM13_PRIORITY},
#endif
};

#if TIM0_ENABLE
static uint64_t timer0_update_counter = 0;
#endif
#if TIM1_ENABLE
static uint64_t timer1_update_counter = 0;
#endif
#if TIM2_ENABLE
static uint64_t timer2_update_counter = 0;
#endif
#if TIM3_ENABLE
static uint64_t timer3_update_counter = 0;
#endif
#if TIM4_ENABLE
static uint64_t timer4_update_counter = 0;
#endif
#if TIM5_ENABLE
static uint64_t timer5_update_counter = 0;
#endif
#if TIM6_ENABLE
static uint64_t timer6_update_counter = 0;
#endif
#if TIM7_ENABLE
static uint64_t timer7_update_counter = 0;
#endif
#if TIM8_ENABLE
static uint64_t timer8_update_counter = 0;
#endif
#if TIM9_ENABLE
static uint64_t timer9_update_counter = 0;
#endif
#if TIM10_ENABLE
static uint64_t timer10_update_counter = 0;
#endif
#if TIM11_ENABLE
static uint64_t timer11_update_counter = 0;
#endif
#if TIM12_ENABLE
static uint64_t timer12_update_counter = 0;
#endif
#if TIM13_ENABLE
static uint64_t timer13_update_counter = 0;
#endif
/*
*/
bool HAL_Timer::init(uint64_t cfg0)
{
  cfg = cfg0;
  uint32_t tmp = HTIM_CFG_EXTRACT(cfg, PORT);
  if(tmp == 0) {  //No port
    return false;
  }
#if HTIM0_ENABLE
  else if(tmp == HHTIM_PORT(0)) {
    port = (uint32_t)TIMER0;
    _counter_bits = 16;
    _update_counter = &timer0_update_counter;
    _index = 0;
  } 
#endif
#if HTIM1_ENABLE
  else if(tmp == HHTIM_PORT(1)) {
    port = (uint32_t)TIMER1;
    _counter_bits = 16;
    _update_counter = &timer1_update_counter;
    _index = 1;
  } 
#endif
#if HTIM2_ENABLE
  else if(tmp == HHTIM_PORT(2)) {
    port = (uint32_t)TIMER2;
    _counter_bits = 16;
    _update_counter = &timer2_update_counter;
    _index = 2;
  } 
#endif
#if HTIM3_ENABLE
  else if(tmp == HHTIM_PORT(3)) {
    port = (uint32_t)TIMER3;
    _counter_bits = 16;
    _update_counter = &timer3_update_counter;
    _index = 3;
  } 
#endif
#if HTIM4_ENABLE
  else if(tmp == HHTIM_PORT(4)) {
    port = (uint32_t)TIMER4;
    _counter_bits = 16;
    _update_counter = &timer4_update_counter;
    _index = 4;
  } 
#endif
#if HTIM5_ENABLE
  else if(tmp == HHTIM_PORT(5)) {
    port = (uint32_t)TIMER5;
    _counter_bits = 16;
    _update_counter = &timer5_update_counter;
    _index = 5;
  } 
#endif
#if HTIM6_ENABLE
  else if(tmp == HHTIM_PORT(6)) {
    port = (uint32_t)TIMER6;
    _counter_bits = 16;
    _update_counter = &timer6_update_counter;
    _index = 6;
  } 
#endif
#if HTIM7_ENABLE
  else if(tmp == HHTIM_PORT(7)) {
    port = (uint32_t)TIMER7;
    _counter_bits = 16;
    _update_counter = &timer7_update_counter;
    _index = 7;
  } 
#endif
#if HTIM8_ENABLE
  else if(tmp == HHTIM_PORT(8)) {
    port = (uint32_t)TIMER8;
    _counter_bits = 16;
    _update_counter = &timer8_update_counter;
    _index = 8;
  } 
#endif
#if HTIM9_ENABLE
  else if(tmp == HHTIM_PORT(9)) {
    port = (uint32_t)TIMER0;
    _counter_bits = 16;
    _update_counter = &timer9_update_counter;
    _index = 9;
  } 
#endif
#if HTIM10_ENABLE
  else if(tmp == HHTIM_PORT(10)) {
    port = (uint32_t)TIMER10;
    _counter_bits = 16;
    _update_counter = &timer10_update_counter;
    _index = 10;
  } 
#endif
#if HTIM11_ENABLE
  else if(tmp == HHTIM_PORT(11)) {
    port = (uint32_t)TIMER11;
    _counter_bits = 16;
    _update_counter = &timer11_update_counter;
    _index = 11;
  } 
#endif
#if HTIM12_ENABLE
  else if(tmp == HHTIM_PORT(12)) {
    port = (uint32_t)TIMER12;
    _counter_bits = 16;
    _update_counter = &timer12_update_counter;
    _index = 12;
  } 
#endif
#if HTIM13_ENABLE
  else if(tmp == HHTIM_PORT(13)) {
    port = (uint32_t)TIMER13;
    _counter_bits = 16;
    _update_counter = &timer13_update_counter;
    _index = 13;
  } 
#endif
  else if(port == 0){
    return false;
  }
          
  return config(cfg);
}
/*
*/
bool HAL_Timer::config(uint64_t cfg0)
{  
  cfg &= ~HTIM_CFG_MASK;
  cfg |= cfg0 & HTIM_CFG_MASK;
  
  clock = (uint32_t)HTIM_CFG_EXTRACT(cfg, CLOCK);
  freq = (uint32_t)HTIM_CFG_EXTRACT(cfg, FREQ);
  
  if(freq >= clock) {
    return false;
  }
  
  if(freq > 0) {
    period = clock / freq;
    if(_counter_bits == 16 && period > 0x10000) {
      return false;
    }
  } else {
    period = 0x10000;
  }
  
  //Attention, timer clock input must = SystemCoreClock
  uint32_t prescalar = SystemCoreClock / clock;  
  if(prescalar > 0x10000) {
    return false;
  }
  
  timer_parameter_struct timer_initpara;
  
  uint32_t tmp = HTIM_CFG_EXTRACT(cfg, CTM);
  if(tmp == HTIM_CTM(UP)) {
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    _counter_dir = 1;
  } else if(tmp == HTIM_CTM(DOWN)) {
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_DOWN;
    _counter_dir = -1;
  } else if(tmp == HTIM_CTM(CA1)) {
    timer_initpara.alignedmode       = TIMER_COUNTER_CENTER_UP;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    _counter_dir = 0;
  } else if(tmp == HTIM_CTM(CA2)) {
    timer_initpara.alignedmode       = TIMER_COUNTER_CENTER_DOWN;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    _counter_dir = 0;
  } else if(tmp == HTIM_CTM(CA3)) {
    timer_initpara.alignedmode       = TIMER_COUNTER_CENTER_BOTH;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    _counter_dir = 0;
  } else {
    return false;
  }
  
  timer_initpara.prescaler         = prescalar - 1;
  timer_initpara.period            = period - 1;
  timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
  timer_initpara.repetitioncounter = 0;
  
  timer_deinit(port);
  timer_init(port, &timer_initpara); 
  
  timer_auto_reload_shadow_enable(port);
  timer_counter_value_config(port, 0);
  
  tmp = HTIM_CFG_EXTRACT(cfg, INT);
  if(tmp == HTIM_INT(ENABLE) && nvic_table[_index].irqn) {
    timer_interrupt_flag_clear(port, TIMER_INT_FLAG_UP);  
    timer_interrupt_enable(port, TIMER_INT_UP);
    nvic_irq_enable(nvic_table[_index].irqn, nvic_table[_index].priority, 0);
  }
  
  timer_enable(port);
  
  return true;
}
/*
*/
uint64_t HAL_Timer::micros()
{
  if(!port || _counter_dir != 1) {
    return 0;
  }
  
  uint32_t sr = enter_critical();
  uint32_t counter1 = TIMER_CNT(port);
  uint32_t flag = TIMER_INTF(port);
  uint32_t counter2 = TIMER_CNT(port);
  if(flag & 0x01) {
    counter1 = counter2 + (1 << _counter_bits);
  }
  exit_critical(sr);
  uint64_t micros = *_update_counter + counter1;
  if(clock == 1000000) {
    return micros;
  } else if(clock > 1000000) {
    return micros / (clock / 1000000);
  } else {
    return micros * (1000000 / clock);
  }
}
/*
*/
void HAL_Timer::attach_irq(void(*fun)(void *param), void *param)
{
  irq_hook[_index] = fun;
  irq_hook_param[_index] = param;
  if(irq_hook[0]) {}        //just use to remove the Warning[Pe550]
  if(irq_hook_param[0]) {}  //just use to remove the Warning[Pe550]
}

/*************************************************
              IRQHandler
**************************************************/
#ifdef __cplusplus
 extern "C" {
#endif
//TIM1
#if TIM0_ENABLE || TIM9_ENABLE
#ifdef GD32F30X_HD
void TIMER0_UP_IRQHandler()
#else
void TIMER0_UP_TIMER9_IRQHandler()
#endif
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
#if TIM0_ENABLE
  if(timer_interrupt_flag_get(TIMER0, TIMER_INT_FLAG_UP) != RESET) {
    uint32_t sr = enter_critical();  //must disable interrupt
    timer_interrupt_flag_clear(TIMER0, TIMER_INT_FLAG_UP);
    timer1_update_counter += 1ll<<16;
    exit_critical(sr);
    if(irq_hook[0] != nullptr) {
      irq_hook[0](irq_hook_param[0]);
    }
  }
#endif
#if defined(GD32F30X_XD) || defined(GD32F30X_CL)
#if TIM9_ENABLE
  if(timer_interrupt_flag_get(TIMER9, TIMER_INT_FLAG_UP) != RESET) {
    uint32_t sr = enter_critical();  //must disable interrupt
    timer_interrupt_flag_clear(TIMER9, TIMER_INT_FLAG_UP);
    timer9_update_counter += 1ll<<16;
    exit_critical(sr);
    if(irq_hook[9] != nullptr) {
      irq_hook[9](irq_hook_param[9]);
    }
  }
#endif
#endif
  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif

//TIM1
#if TIM1_ENABLE
void TIM1_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(timer_interrupt_flag_get(TIMER1, TIMER_INT_FLAG_UP) != RESET) {
    uint32_t sr = enter_critical();  //must disable interrupt
    timer_interrupt_flag_clear(TIMER1, TIMER_INT_FLAG_UP);
    timer1_update_counter += 1ll<<16;
    exit_critical(sr);
    if(irq_hook[1] != nullptr) {
      irq_hook[1](irq_hook_param[1]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif

//TIM2
#if TIM2_ENABLE
void TIM2_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_UP) != RESET) {
    uint32_t sr = enter_critical();  //must disable interrupt
    timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP);
    timer2_update_counter += 1ll<<16;
    exit_critical(sr);
    if(irq_hook[2] != nullptr) {
      irq_hook[2](irq_hook_param[2]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif

//TIM3
#if TIM3_ENABLE
void TIM3_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(timer_interrupt_flag_get(TIMER3, TIMER_INT_FLAG_UP) != RESET) {
    uint32_t sr = enter_critical();  //must disable interrupt
    timer_interrupt_flag_clear(TIMER3, TIMER_INT_FLAG_UP);
    timer3_update_counter += 1ll<<16;
    exit_critical(sr);
    if(irq_hook[3] != nullptr) {
      irq_hook[3](irq_hook_param[3]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif

//TIM4
#if TIM4_ENABLE
void TIM4_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(timer_interrupt_flag_get(TIMER4, TIMER_INT_FLAG_UP) != RESET) {
    uint32_t sr = enter_critical();  //must disable interrupt
    timer_interrupt_flag_clear(TIMER4, TIMER_INT_FLAG_UP);
    timer4_update_counter += 1ll<<16;
    exit_critical(sr);
    if(irq_hook[4] != nullptr) {
      irq_hook[4](irq_hook_param[4]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif

//TIM5
#if TIM5_ENABLE
void TIM5_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(timer_interrupt_flag_get(TIMER5, TIMER_INT_FLAG_UP) != RESET) {
    uint32_t sr = enter_critical();  //must disable interrupt
    timer_interrupt_flag_clear(TIMER5, TIMER_INT_FLAG_UP);
    timer5_update_counter += 1ll<<16;
    exit_critical(sr);
    if(irq_hook[5] != nullptr) {
      irq_hook[5](irq_hook_param[5]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif

//TIM6
#if TIM6_ENABLE
void TIM6_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(timer_interrupt_flag_get(TIMER6, TIMER_INT_FLAG_UP) != RESET) {
    uint32_t sr = enter_critical();  //must disable interrupt
    timer_interrupt_flag_clear(TIMER6, TIMER_INT_FLAG_UP);
    timer6_update_counter += 1ll<<16;
    exit_critical(sr);
    if(irq_hook[6] != nullptr) {
      irq_hook[6](irq_hook_param[6]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif

//TIM7
#if TIM7_ENABLE || TIM12_ENABLE
#ifdef GD32F30X_HD
void TIMER7_IRQHandler()
#else
void TIMER7_UP_TIMER12_IRQHandler()
#endif
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
#if TIM7_ENABLE
  if(timer_interrupt_flag_get(TIMER7, TIMER_INT_FLAG_UP) != RESET) {
    uint32_t sr = enter_critical();  //must disable interrupt
    timer_interrupt_flag_clear(TIMER7, TIMER_INT_FLAG_UP);
    timer7_update_counter += 1ll<<16;
    exit_critical(sr);
    if(irq_hook[7] != nullptr) {
      irq_hook[7](irq_hook_param[7]);
    }
  }
#endif
#if defined(GD32F30X_XD) || defined(GD32F30X_CL)
#if TIM12_ENABLE
  if(timer_interrupt_flag_get(TIMER12, TIMER_INT_FLAG_UP) != RESET) {
    uint32_t sr = enter_critical();  //must disable interrupt
    timer_interrupt_flag_clear(TIMER12, TIMER_INT_FLAG_UP);
    timer12_update_counter += 1ll<<16;
    exit_critical(sr);
    if(irq_hook[12] != nullptr) {
      irq_hook[12](irq_hook_param[12]);
    }
  }
#endif
#endif
  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif

//TIM8
#if TIM8_ENABLE
#if defined(GD32F30X_XD) || defined(GD32F30X_CL)
void TIMER0_BRK_TIMER8_IRQHandler()
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(timer_interrupt_flag_get(TIMER8, TIMER_INT_FLAG_UP) != RESET) {
    uint32_t sr = enter_critical();  //must disable interrupt
    timer_interrupt_flag_clear(TIMER8, TIMER_INT_FLAG_UP);
    timer8_update_counter += 1ll<<16;
    exit_critical(sr);
    if(irq_hook[8] != nullptr) {
      irq_hook[8](irq_hook_param[8]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
#endif

//TIM10
#if TIM10_ENABLE
#if defined(GD32F30X_XD) || defined(GD32F30X_CL)
void TIMER0_TRG_CMT_TIMER10_IRQHandler()
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(timer_interrupt_flag_get(TIMER10, TIMER_INT_FLAG_UP) != RESET) {
    uint32_t sr = enter_critical();  //must disable interrupt
    timer_interrupt_flag_clear(TIMER10, TIMER_INT_FLAG_UP);
    timer10_update_counter += 1ll<<16;
    exit_critical(sr);
    if(irq_hook[10] != nullptr) {
      irq_hook[10](irq_hook_param[10]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
#endif

//TIM11
#if TIM11_ENABLE
#if defined(GD32F30X_XD) || defined(GD32F30X_CL)
void TIMER7_BRK_TIMER11_IRQHandler()
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(timer_interrupt_flag_get(TIMER11, TIMER_INT_FLAG_UP) != RESET) {
    uint32_t sr = enter_critical();  //must disable interrupt
    timer_interrupt_flag_clear(TIMER11, TIMER_INT_FLAG_UP);
    timer11_update_counter += 1ll<<16;
    exit_critical(sr);
    if(irq_hook[11] != nullptr) {
      irq_hook[11](irq_hook_param[11]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
#endif

//TIM13
#if TIM13_ENABLE
#if defined(GD32F30X_XD) || defined(GD32F30X_CL)
void TIMER7_TRG_CMT_TIMER13_IRQHandler()
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(timer_interrupt_flag_get(TIMER13, TIMER_INT_FLAG_UP) != RESET) {
    uint32_t sr = enter_critical();  //must disable interrupt
    timer_interrupt_flag_clear(TIMER13, TIMER_INT_FLAG_UP);
    timer13_update_counter += 1ll<<16;
    exit_critical(sr);
    if(irq_hook[13] != nullptr) {
      irq_hook[13](irq_hook_param[13]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
#endif
/*
*/
#ifdef __cplusplus
}
#endif




